/*
 * DIM-SUM操作系统 - 锁
 *
 * Copyright (C) 2022 国科础石(重庆)软件有限公司
 *
 * 作者: Dong Peng <w-pengdong@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#ifndef __ASM_SMP_RWLOCK_H
#define __ASM_SMP_RWLOCK_H

#include <linux/compiler.h>
#include <asm/fence.h>

struct arch_smp_rwlock {
	unsigned int lock;
} __attribute__((aligned(4)));

#define __ARCH_SMP_RWLOCK_UNLOCKED		{ 0 }

#define arch_smp_rwlock_can_read(x)			((x)->lock < 0x80000000)
#define arch_smp_rwlock_can_write(x)		((x)->lock == 0)

static inline void arch_smp_read_lock(struct arch_smp_rwlock *lock)
{
	u32 tmp, lockval;

	__asm__ __volatile__(
		"1:	lr.w	%[lockval], %[lock]\n"
		"	addi	%[lockval], %[lockval], 1\n"
		"   srli 	%[tmp], %[lockval], 31 \n"
		"   andi 	%[tmp], %[tmp], 1 \n" //31bit = 1 goto 1b
		"	bnez    %[tmp], 1b \n"
		"	sc.w	%[tmp], %[lockval], %[lock]\n"
		"	bnez	%[tmp], 1b\n"
		RISCV_ACQUIRE_BARRIER
		: [lock]"+A" (lock->lock), [lockval]"=&r" (lockval), [tmp]"=&r" (tmp)
		:: "memory");
}

static inline void arch_smp_write_lock(struct arch_smp_rwlock *lock)
{
	u32 lockval, tmp;

	__asm__ __volatile__(
		"1:	lr.w	%[lockval], %[lock]\n"
		"	bnez	%[lockval], 1b\n"
		"	sc.w	%[tmp], %[w_val], %[lock]\n"
		"	bnez	%[tmp], 1b\n"
		RISCV_ACQUIRE_BARRIER
		: [lock]"+A" (lock->lock), [lockval]"=&r" (lockval), [tmp]"=&r" (tmp)
		: [w_val]"r" (0x80000000)
		: "memory"
	);
}

static inline int arch_smp_tryread(struct arch_smp_rwlock *lock)
{
	u32 tmp, lockval, res = 1;

	__asm__ __volatile__(
		"	lr.w	%[lockval], %[lock]\n"
		"	addi	%[lockval], %[lockval], 1\n"
		"   srli 	%[tmp], %[lockval], 31 \n"
		"   andi 	%[tmp], %[tmp], 1 \n" //31bit = 1 goto 1f
		"	bnez    %[tmp], 1f \n"
		"	sc.w	%[res], %[lockval], %[lock]\n"
		RISCV_ACQUIRE_BARRIER
		"1:\n"
		: [lock]"+A" (lock->lock), [lockval] "=&r" (lockval), [tmp]"=&r" (tmp), [res]"=&r" (res)
		:: "memory");

	return !res;
}

static inline int arch_smp_trywrite(struct arch_smp_rwlock *lock)
{
	u32 tmp;

	__asm__ __volatile__(
		"1:	lr.w	%[tmp], %[lock]\n"
		"	bnez	%[tmp], 2f\n"
		"	sc.w	%[tmp], %[w_val], %[lock]\n"
		"	bnez	%[tmp], 1b\n"
		RISCV_ACQUIRE_BARRIER
		"2:\n"
		: [lock]"+A" (lock->lock), [tmp]"=&r" (tmp)
		: [w_val]"r" (0x80000000)
		: "memory");

	return !tmp;
}

static inline void arch_smp_read_unlock(struct arch_smp_rwlock *lock)
{
	u32 tmp, lockval;
	__asm__ __volatile__(
		RISCV_RELEASE_BARRIER
		"1:	lr.w	%[lockval], %[lock]\n"
		"	addi	%[lockval], %[lockval], -1\n" // lockval-=1
		"	sc.w	%[tmp], %[lockval], %[lock]\n"
		"	bnez	%[tmp], 1b\n"
		: [lock]"+A" (lock->lock), [tmp]"=&r" (tmp), [lockval]"=&r" (lockval)
		:
		: "memory");
}

static inline void arch_smp_write_unlock(struct arch_smp_rwlock *lock)
{
	WRITE_ONCE(lock->lock, 0);
}
#endif /* __ASM_SMP_RWLOCK_H */
